﻿using System;
using biz.ritter.javapi.io;
using biz.ritter.javapi.lang;
using biz.ritter.javapi.lang.annotation;
using biz.ritter.javapi.net;
using biz.ritter.javapi.util;
using Nofs.Net.nofs.metadata.interfaces;

namespace Nofs.Net.AnnotationDriver
{
    public class NoFSClassLoader : INoFSClassLoader
    {
        private ClassLoader _loader;
        private Map<String, Class> _classCache;
        private Map<Type, LinkedList<Class>> _annotationCache;
        private bool _loadedAll = false;

        public NoFSClassLoader(String fileName) //throws MalformedURLException 
            : this()
        {
            URL[] url = new URL[] { new URL(fileName) };
            _loader = new URLClassLoader(url, ClassLoader.getSystemClassLoader());
        }

        public NoFSClassLoader(ClassLoader loader)
            : this()
        {

            _loader = loader;
        }

        private NoFSClassLoader()
        {
            _classCache = new HashMap<String, Class>();
            _annotationCache = new HashMap<Type, LinkedList<Class>>();
        }

        public Class LoadClass(String className) //throws ClassNotFoundException 
        {
            Class cls = null;
            if (_classCache.containsKey(className))
            {
                cls = _classCache.get(className);
            }
            else
            {
                cls = _loader.loadClass(className);
                _classCache.put(className, cls);
                Console.WriteLine("loading class: " + cls.getName());
            }
            return cls;
        }

        public LinkedList<Class> LoadClassesWithAnnotation(MarkerTypes markerType) //throws ClassNotFoundException, Exception 
        {
            Type annotationType = MarkerToAnnotationTranslator.ToAnnotation(markerType);
            LinkedList<Class> classes;
            if (!_annotationCache.containsKey(annotationType))
            {
                classes = new LinkedList<Class>();
                if (!_loadedAll)
                {
                    LoadAllClasses();
                }

                foreach (Class cls in new LinkedList<Class>(_classCache.values()))
                {
                    foreach (Attribute annotation in cls.getAnnotations())
                    {
                        if (annotation.GetType() == annotationType)
                        {
                            if (!classes.contains(cls))
                            {
                                classes.add(cls);
                                Console.WriteLine("marking class " + cls.getName() + " as having annotation " + markerType.ToString());
                            }
                        }
                    }
                }
                _annotationCache.put(annotationType, classes);
            }
            else
            {
                classes = _annotationCache.get(annotationType);
            }
            return new LinkedList<Class>(classes);
        }

        //@SuppressWarnings("unchecked")
        private void LoadAllClasses() //throws ClassNotFoundException 
        {
            Map<String, Set<Class>> classes = ClassList.findClasses(_loader);
            foreach (String interfaceName in new LinkedList<String>(classes.keySet()))
            {
                foreach (Class cls in new LinkedList<Class>(classes.get(interfaceName)))
                {
                    if (!_classCache.containsKey(cls.getName()))
                    {
                        _classCache.put(cls.getName(), cls);
                        Console.WriteLine("loading class: " + cls.getName());
                    }
                }
            }
            _loadedAll = true;
        }

        public Object Create(String className) //throws InstantiationException, IllegalAccessException, ClassNotFoundException 
        {
            return LoadClass(className).newInstance();
        }
    }

}
